from flask import Blueprint, jsonify, request, redirect, url_for, flash, render_template_string
from wtforms import Form, StringField, PasswordField, validators

class RegistrationForm(Form):
    username = StringField('Username', [
        validators.DataRequired(),
        validators.Length(min=4, max=25)
    ])
    password = PasswordField('Password', [
        validators.DataRequired(),
        validators.Length(min=6, max=35),
        validators.EqualTo('confirm', message='Passwords must match')
    ])
    confirm = PasswordField('Repeat Password')

from config import GlobalContextData
from db.user import User
from db.login import Login
from app.db import Database
from app.api_tools import flask_tool
from app.log import logger
from app.middleware import require_auth_static_user

from uuid import uuid4
from pathlib import Path

app = Blueprint('user', __name__, url_prefix='/api/v1/user')

login_dir = Path.cwd() / 'static' / 'login'

@app.route('/login/<username>/verify', methods=['GET'])
def login_username_if_exists(username):
    """
    Verify if a username exists in the database.
    ---
    tags:
      - Authentication
    parameters:
      - name: username
        in: path
        type: string
        required: true
        description: The username to verify.
    responses:
      200:
        description: A JSON object indicating if the username exists.
        schema:
          type: object
          properties:
            is_exists:
              type: boolean
              description: True if the username exists, False otherwise.
              example: True
            auth_uuid:
              type: string
              description: A UUID for caching the username, null if it doesn't exist.
              example: "550e8400-e29b-41d4-a716-446655440000"
            message:
              type: string
              description: The operation message
              example: ""
    """

    logger.info(f'User {username} start to login.')
    session = Database.get_session()
    is_exists = session.query(User).filter_by(username=username).count()

    auth_uuid = str(uuid4()) if is_exists else None

    # 如果用户存在，则缓存用户名和UUID关系
    if is_exists:
        cache_key = f"auth_uuid:{auth_uuid}"
        flask_tool.cache.set(cache_key, username, timeout=5 * 60)
        logger.info(f'Set login auth id for {username}: {auth_uuid}')
    else:
        logger.info(f'Not found user: {username}')

    response_data = {
        'is_exists': bool(is_exists),
        'auth_uuid': auth_uuid,
        'message': '' if is_exists else '找不到该用户名'
    }

    return jsonify(response_data)

@app.route('/login/<uuid>/pwd', methods=['POST'])
def login_paasword_by_auth_id(uuid):
    """
        Login using authentication ID and password
        ---
        tags:
          - Authentication
        parameters:
          - name: uuid
            in: path
            type: string
            required: true
            description: The unique identifier for the user.
          - in: body
            name: body
            required: true
            description: The login request payload.
            schema:
              type: object
              properties:
                pwd:
                  type: string
                  description: The password for the user account.
                  example: your_password_here
        responses:
          200:
            description: Login successful
            schema:
              type: object
              properties:
                message:
                  type: string
                  example: 登录成功
                access_token:
                  type: string
                  description: The access token for the session.
          400:
            description: Bad request or login failed due to incorrect password
            schema:
              type: object
              properties:
                message:
                  type: string
                  example: 登录失败，密码错误。
          404:
            description: User not found in cache
            schema:
              type: object
              properties:
                message:
                  type: string
                  example: 正在尝试访问一个未知的资源。
    """
    if 'pwd' not in request.json:
        logger.warning('Get a warning login request.' )
        return jsonify({ 'message': '未知的请求。' }), 400
    password: str = request.json['pwd']
    username: str = flask_tool.cache.get('auth_uuid:' + uuid)
    if not username:
        logger.warning(f'Not found the username in flask-cache: {username}')
        return jsonify({ 'message': '正在尝试访问一个未知的资源。' }), 404

    session = Database.get_session()
    user = session.query(User).filter_by(username=username).one()
    if user.password_hash != user.generate_password_hash(password=password):
        logger.info(f'{username} login failed. password error: {password}')
        return jsonify({ 'message': '登录失败，密码错误。' }), 400

    flask_tool.cache.delete('auth_uuid:' + uuid)
    logger.info(f'{username} password verify success.')
    login = Login(user_id=user.id, last_ip=str(request.remote_addr), user_agent=str(request.user_agent))
    session.add(login)
    session.commit()

    has_logins = session.query(Login).filter_by(user_id=user.id).order_by(Login.created_at).all()
    if len(has_logins) > user.max_login:
        logger.info(f'{username} has exists login > the user max login count: {user.max_login}')
        num_to_delete = len(has_logins) - user.max_login
        for delete_login in has_logins[:num_to_delete]:
            logger.info(f'remove token {delete_login.token}, ip {delete_login.last_ip} for {username}')
            session.delete(delete_login)

    return jsonify({
        'message': '登录成功',
        'access_token': login.token
    })

# http://localhost:5000/api/v1/user/register
@app.route('/register', methods=['GET', 'POST'])
@require_auth_static_user(username='root', password='root')
def register():
    form = RegistrationForm(request.form)

    if request.method == 'POST' and form.validate():
        username = form.username.data
        password = form.password.data

        session = Database.get_session()
        user = session.query(User).filter_by(username=username).first()
        if user:
            flash('该用户名已存在', 'danger')
        else:
            new_user = User(username=username, password=password)
            session.add(new_user)
            session.commit()

            flash('注册成功', 'success')
            return redirect(url_for('user.register'))

    return render_template_string((login_dir / 'register.html').read_text('utf-8'), form=form)